import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { filter, map } from 'rxjs/operators';

import { RefreshTokenKey, TokenKey } from '@app/config/constant';
import { environment } from '@env/environment';
import { localUrl } from '@env/environment.prod';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { NzMessageService } from 'ng-zorro-antd/message';
import * as qs from 'qs';

export enum StatusCode {
  Ok = 200,
  Created = 201,
  UnAuthorized = 401,
  Correct = 0
}

export interface HttpCustomConfig {
  needSuccessInfo?: boolean; // 是否需要"操作成功"提示
  showLoading?: boolean; // 是否需要loading
  otherUrl?: boolean; // 是否是第三方接口
}

export interface ActionResult<T> {
  statuscode: number;
  msg: string;
  items: T;
}

export interface PageResult<T> {
  filteredrecordcount: number;
  items: T[];
  msg: string;
  statuscode: number;
  totalrecordcount: number;
}

export interface PageItem<T> {
  records: T[];
  size: number;
  total: number;
  pages: number;
}

@Injectable({
  providedIn: 'root'
})
export class BaseHttpService {
  uri: string;
  http = inject(HttpClient);
  message = inject(NzMessageService);

  protected constructor(private readonly router: Router) {
    this.uri = environment.production ? localUrl : '/daemonApi';
  }

  get<T>(path: string, param?: NzSafeAny, config?: HttpCustomConfig, resolveResult: boolean = true): Observable<T> {
    config = config || { needSuccessInfo: false };
    const reqPath = this.getUrl(path, config);
    const params = new HttpParams({ fromString: qs.stringify(param) });
    const result = this.http.get<ActionResult<T>>(reqPath, { params });
    return result.pipe(this.resultHandle<T>(config));
  }

  getPage<T>(path: string, param?: NzSafeAny, config?: HttpCustomConfig): Observable<T> {
    config = config || { needSuccessInfo: false };
    const reqPath = this.getUrl(path, config);
    const params = new HttpParams({ fromString: qs.stringify(param) });
    return this.http.get<PageResult<T>>(reqPath, { params }).pipe(this.resultPageHandle<T>(config));
  }

  delete<T>(path: string, param?: NzSafeAny, config?: HttpCustomConfig): Observable<T> {
    config = config || { needSuccessInfo: false };
    const reqPath = this.getUrl(path, config);
    const params = new HttpParams({ fromString: qs.stringify(param) });
    return this.http.delete<ActionResult<T>>(reqPath, { params }).pipe(this.resultHandle<T>(config));
  }

  post<T>(path: string, param?: NzSafeAny, config?: HttpCustomConfig): Observable<T> {
    config = config || { needSuccessInfo: false };
    const reqPath = this.getUrl(path, config);
    return this.http.post<ActionResult<T>>(reqPath, param).pipe(this.resultHandle<T>(config));
  }

  postPage<T>(path: string, param?: NzSafeAny, config?: HttpCustomConfig): Observable<PageResult<T>> {
    config = config || { needSuccessInfo: false };
    const reqPath = this.getUrl(path, config);
    return this.http.post<PageResult<T>>(reqPath, param).pipe(this.resultPostPageHandle<T>(config));
  }

  put<T>(path: string, param?: NzSafeAny, config?: HttpCustomConfig): Observable<T> {
    config = config || { needSuccessInfo: false };
    const reqPath = this.getUrl(path, config);
    return this.http.put<ActionResult<T>>(reqPath, param).pipe(this.resultHandle<T>(config));
  }

  patch<T>(path: string, param?: NzSafeAny, config?: HttpCustomConfig): Observable<T> {
    config = config || { needSuccessInfo: false };
    const reqPath = this.getUrl(path, config);
    return this.http.patch<ActionResult<T>>(reqPath, param).pipe(this.resultHandle<T>(config));
  }

  upload<T>(path: string, param?: NzSafeAny, config?: HttpCustomConfig): Observable<T> {
    config = config || { needSuccessInfo: false };
    const reqPath = this.getUrl(path, config);
    let httpHeaders = new HttpHeaders();
    httpHeaders.set('Content-Type', 'multipart/form-data; boundary=----WebKitFormBoundarysESkjVoAaxHWYtkW');
    return this.http.post<ActionResult<T>>(reqPath, param, { headers: httpHeaders }).pipe(this.resultHandle<T>(config));
  }

  downLoadWithBlob(path: string, param?: NzSafeAny, config?: HttpCustomConfig): Observable<NzSafeAny> {
    config = config || { needSuccessInfo: false };
    const reqPath = this.getUrl(path, config);
    return this.http.post(reqPath, param, {
      responseType: 'blob',
      headers: new HttpHeaders().append('Content-Type', 'application/json')
    });
  }

  getUrl(path: string, config: HttpCustomConfig): string {
    const specialUrl: string[] = ['/sysPermission/menu/1'];
    let reqPath = this.uri + path;
    if (config.otherUrl) {
      reqPath = path;
    }
    return specialUrl.includes(path) ? `/site/api${path}` : reqPath;
  }

  resultHandle<T>(config: HttpCustomConfig): (observable: Observable<ActionResult<T>>) => Observable<T> {
    return (observable: Observable<ActionResult<T>>) => {
      return observable.pipe(
        filter(item => {
          return this.handleFilter(item, !!config.needSuccessInfo);
        }),
        map(item => {
          const { statuscode } = item;
          const correctStatusCode: number[] = [StatusCode.Ok, StatusCode.Created, StatusCode.UnAuthorized, StatusCode.Correct];
          if (!correctStatusCode.includes(statuscode)) {
            throw new Error(item.msg);
          } else if (statuscode === 401) {
            this.handleLoginOut();
          }
          return item.items;
        })
      );
    };
  }

  resultPostPageHandle<T>(config: HttpCustomConfig): (observable: Observable<PageResult<T>>) => Observable<PageResult<T>> {
    return (observable: Observable<PageResult<T>>) => {
      return observable.pipe(
        filter(item => {
          return this.handleFilter(item, !!config.needSuccessInfo);
        }),
        map(item => {
          const { statuscode } = item;
          const correctStatusCode: number[] = [StatusCode.Ok, StatusCode.Created, StatusCode.UnAuthorized, StatusCode.Correct];
          if (!correctStatusCode.includes(statuscode)) {
            throw new Error(item.msg);
          } else if (statuscode === 401) {
            this.handleLoginOut();
          }

          return item;
        })
      );
    };
  }

  resultPageHandle<T>(config: HttpCustomConfig): (observable: Observable<PageResult<T>>) => Observable<T> {
    return (observable: Observable<PageResult<T>>) => {
      return observable.pipe(
        filter(item => {
          return this.handleFilter(item, !!config.needSuccessInfo);
        }),
        map(item => {
          const { statuscode, msg } = item;
          const correctStatusCode: number[] = [StatusCode.Ok, StatusCode.Created, StatusCode.UnAuthorized, StatusCode.Correct];
          if (!correctStatusCode.includes(statuscode)) {
            throw new Error(msg);
          } else if (statuscode === 401) {
            this.handleLoginOut(msg);
          }

          return item.items[0];
        })
      );
    };
  }

  handleFilter<T>(item: ActionResult<T>, needSuccessInfo: boolean): boolean {
    const excludeStatusCode: number[] = [StatusCode.Created, StatusCode.Ok, StatusCode.Correct, StatusCode.UnAuthorized];
    if (!excludeStatusCode.includes(item.statuscode)) {
      this.message.error(item.msg);
    } else if (StatusCode.UnAuthorized == item.statuscode) {
      this.handleLoginOut(item.msg);
      return false;
    } else if (needSuccessInfo) {
      this.message.success('操作成功');
    }
    return true;
  }

  handleLoginOut(msg: string = ''): void {
    if (this.router.url !== '/login/login-form') {
      this.message.error('请重新登录！');
      this.router.navigateByUrl('/login');
      sessionStorage.removeItem(TokenKey);
      sessionStorage.removeItem(RefreshTokenKey);
    } else {
      this.message.error(msg);
    }
  }
}
